package optimization;

import java.util.*;

import temp.Temp;
import util.List;
import assem.Instr;
import flow_graph.AssemFlowGraph;
import graph.Node;

public class ConstPropagation {

	private ReachingDefinition dfa;
	private AssemFlowGraph cfg;
	boolean control = true;
	
	/*
	 * Realiza a otimização Constant Propagation
	 */
	public void optimize(List<Instr> l) {
		
		cfg = new AssemFlowGraph(l);
		dfa = new ReachingDefinition(l, cfg);

		//Variável de controle do loop, que pára as iterações quando
		//o valor das variáveis estabilizam
        
        while (control) {
        	
        	control = false;
        	
        	System.out.println("***************** ITERACAO *******************\n");
        	
        	List<Node> nodes = cfg.nodes();
    		Iterator<Node> iter = nodes.iterator();
    		while(iter.hasNext()){
    			Node node = (Node) iter.next();
    			
    			 // Itera em todos os registradores usados nesse nó node. 
    			 //Vemos onde esses registradores foram definidos e se possivelmente
    			 //podemos fazer a substituição na definição feita nesse nó node diretamente pela constante
    			 
    			 
    			 List<Temp> temps = cfg.getUsed(node);
    			 
    			 if (temps != null) {
	    			 Iterator<Temp> iter2 = temps.iterator();
	    			 
	    			 while(iter2.hasNext()){
	    				 Temp temp = (Temp) iter2.next();
	    				 //set de nós onde são feitas todas as definições de "temp".
	    				 //queremos achar se é feita alguma atribuição de constante a "temp"
	    				 Set<Node> snode = dfa.getDefs(temp);
	    				 if (snode != null) {
	    					 // Verificamos quais definições são feitas nas entradas do nó atual
	    					 Set<Node> defs_ent = dfa.getIn(node);
	    					 String cte = findConstant(defs_ent,temp);
	    					 if(cte != null){
	    						 propagateConstant(cfg.getInstr(node), cte);
	    					 }
	    					 
	    				 }
	    			 }
	    			 
    			 }
    		}
    		
    		System.out.println("********************************************************\n");
    		
        }
		
	}
	
	private String findConstant(Set<Node> setNode,Temp temp){
		
		int tempDefined = 0;
		Node nodeUsed = null;
		
		Iterator<Node> iterNode = setNode.iterator();
		while(iterNode.hasNext()){
			Node nodeNext = iterNode.next();
			List<Temp> tempList = cfg.getDefined(nodeNext);
			if(tempList.contains(temp)){
				tempDefined++;
				nodeUsed = nodeNext;
			}
		}
		if(tempDefined == 1 && nodeUsed != null){
			if (cfg.getUsed(nodeUsed) == null && cfg.getInstr(nodeUsed).getAssembly().contains("mov")) {
				//caso em que há apenas um nó de entrada e definição de temp por constante
				//System.out.println("Intr: " + cfg.getInstr(nodeUsed).getAssembly() + "\n");	
				String getConst = getConstant(cfg.getInstr(nodeUsed));
				return getConst;
			}
		}
		return null;
	}

	private void propagateConstant(Instr instr, String cte) {
		
		String assembly = instr.getAssembly();
		if((!(assembly.contains("dword")))){
			System.out.println("CTE -- " + cte + " // " + "ASM -- " + assembly);
			
			//apagar essa porra depois
			String oldAsm = assembly;
			
			int index = assembly.indexOf(",");
			String newAssembly = assembly.substring(0, index + 2) + cte;
			instr.setAssembly(newAssembly.replaceAll("mov", "mov dword"));
			
			// apagar essa porra depois
			System.out.println("Mudou DE \"" + oldAsm + "\" PARA \"" + instr.getAssembly() + "\"");
			
			if(!(newAssembly.equals(assembly)))
				control = true;
		}
		
	}

	private String getConstant(Instr instruction) {
		String instr = instruction.getAssembly();
		int index = instr.indexOf(",");
		return instr.substring(index + 1, instr.length());
	}
}
